const Decimal = require('decimal.js')
const ccxt = require("ccxt")
const moment = require('moment')
// const chalk = require('chalk')
const TI = require('trading-indicator')
const {
    OrderDirection,
    PositionSide,
    StoreName,
    BollStatus,
    OrderDirectionFactor,
    OrderType,
    BuildConfig,
    OrderTypeUpper,
    OrderStatus,
    Symbol
} = require('../constants')
const LogService = require('./LogService')
const MathModel = require('./MathModel')
const print = LogService.print
const _ = require('lodash')

const log = console.log

class Api {
    constructor () {
        this.state = {}
    }
    requestKlinesV2 () {
        const { exchangeId: ex, tick, interval } = this.state
        let exchangeId = ex,
            exchangeClass = ccxt[exchangeId]
        let exchange = new exchangeClass({})
        return exchange.fetchOHLCV(tick, interval)
        .then(klineList => {
            const formatedKlineList = klineList.slice(-50).map(item => ({
                time: moment(item[0]).format(),
                timeStamp: item[0],
                openPrice: item[1],
                highestPrice: item[2],
                lowestPrice: item[3],
                closePrice: item[4],
                volumn: item[5],
                middilePrice: Decimal(item[1]).add(item[4]).div(2).toNumber(),
                color: item[1] > item[4] ? 'red': 'green'
            }))
            return formatedKlineList
        })
    }
    setState (state) {
        this.$exchange = state.exchange
        this.state = state
    }
    addState (state) {
        this.state = {
            ...this.state,
            ...state
        }
    }
    /**
     * 获取建仓列表的订单情况
     */
    requestBuildOrderList () {
        const cacheBuildList = LogService.getJson(StoreName.BUILD_LIST) || []
        const orderIdList = cacheBuildList.map(item => Number(_.get(item, 'openInfo.id')))
        
        const { symbol } = this.state
        return this.$exchange.fapiPrivateGetAllOrders({
            symbol
        }).then(orderList => {
            const filterOrderList = orderList.filter(item => orderIdList.includes(Number(item.orderId)))
            return filterOrderList.sort((a, b) => {
                if (a.side === OrderDirection.BUY) {
                    return a.price > b.price ? -1 : 1
                } else {
                    return a.price < b.price ? -1 : 1
                }
            })
        })
    }
    /**
     * 获取当前最新价格
     * @returns price
     */
    requestPrice () {
        const { symbol } = this.state
        return this.$exchange.fapiPublicGetTickerPrice({ symbol })
            .then(item => Number(item.price))
    }
    /**
     * 获取最新现货价格
     * @returns price
     */
    requestPublicPrice () {
        const { symbol } = this.state
        return this.$exchange.publicGetTickerPrice({ symbol })
            .then(item => Number(item.price))
    }
    /**
     * 查询账户余额
     * @returns 余额信息: info
     */
    requestBalance () {
        return this.$exchange.fapiPrivateV2GetBalance().then(balanceInfo => {
            const usdtBalance = balanceInfo.find(item => item.asset === 'USDT')
            // 可用余额
            const {
            // 总余额
            balance,
            // 可用余额（未下单的钱）
            availableBalance,
            // 未持仓余额
            crossWalletBalance,
            // 持仓金额
            crossUnPnl
            } = usdtBalance

            // 持仓金额
            // const holdingAmount = Decimal(balance)
            // .sub(crossWalletBalance)
            // .toNumber()
            const holdingAmount = _.toNumber(crossUnPnl)

            // 挂单金额
            const openOrderAmount = Decimal(crossWalletBalance)
            .sub(availableBalance)
            .toNumber()
            return {
                balance: _.toNumber(balance),
                holdingAmount: _.toNumber(holdingAmount),
                openOrderAmount: _.toNumber(openOrderAmount),
                availableBalance: Number(availableBalance),
                isFree: balance === availableBalance
            }
        })
    }
    /**
     * 查询开单列表
     * @returns 开单列表
     */
    requestOpenOrders () {
        const { tick } = this.state
        return this.$exchange.fetchOpenOrders(tick)
            .then(openOrders => {
                const realOpenOrders = openOrders.filter(order => order.type === 'limit')
                const stopOrders = openOrders.filter(order =>  {
                   if ( ['take_profit_market', 'stop_market'].includes(order.type)) {
                       return true
                   }
                })
                if (stopOrders.length > 6) {
                    this.$exchange.cancelOrder(stopOrders[stopOrders.length - 1].id, Symbol)
                    this.$exchange.cancelOrder(stopOrders[stopOrders.length - 2].id, Symbol)
                    this.$exchange.cancelOrder(stopOrders[stopOrders.length - 3].id, Symbol)
                    this.$exchange.cancelOrder(stopOrders[stopOrders.length - 4].id, Symbol)
                }
                return realOpenOrders
            })
    }
    /**
     * 查询开单列表
     * @returns 开单列表
     */
    requestHoldingOrder () {
        const { symbol } = this.state
        return this.$exchange.fapiPrivateGetAllOrders({
            symbol
        }).then(orderList => {
            const filteredOrderList = orderList.filter(item => item.status === 'FILLED' && item.origType === 'LIMIT') || []
            // return filteredOrderList[filteredOrderList.length - 1]
            return { holdingDirection: _.toLower(_.get(filteredOrderList, '[filteredOrderList.length - 1].side')) }
        })
    }
    /**
     * 查询止盈列表
     * @returns 开单列表
     */
    requestOrderByType (orderTypeList, orderStatusList) {
        const { symbol } = this.state
        return this.$exchange.fapiPrivateGetAllOrders({
            symbol
        }).then(orderList => {
            return orderList.filter(item => orderTypeList.includes(item.origType) && orderStatusList.includes(item.status))
        })
    }
    /**
     * 取消多余止盈单
     * @returns orderList
     */
    cancelSomeStopOrders () {
        return this.requestOrderByType([
            OrderTypeUpper.STOP, 
            OrderTypeUpper.TAKE_PROFIT
        ], [
            OrderStatus.NEW
        ]).then(orderList => {
            const sortOrderList = orderList.sort((a, b) => {
                return a.orderId > b.orderId ? 1 : -1
            })
            const orderIdList = sortOrderList.slice(0, sortOrderList.length - 6).map(item => item.orderId)
            if (orderIdList.length) {
                Promise.all(
                    orderIdList.map(item => this.$exchange.fapiPrivateDeleteOrder({
                        symbol: this.state.symbol,
                        orderId: item
                    }))
                ).then(orderList => {
                    print('取消多余止盈单成功', 'order')
                    console.log('取消多余止盈单成功', orderList.map(item => item.orderId))
                }).catch(err => {
                    print(`取消多余止盈单失败，原因：${err}`, 'order')
                    console.log('取消多余止盈单失败，原因：', err)
                })
            } else {
                return Promise.resolve([])
            }
        })
    }
    /**
     * 查询布林带数据
     * @returns { 本次和上一次布林项，以及布林带列表 }
     */
    requestBoll () {
        const { exchangeId, tick, interval } = this.state
        return TI.bb(20, 2, 'close', exchangeId, tick, interval, true)
            .then(bollList => {
                const {
                    upper: bollUp,
                    middle: bollMiddle,
                    lower: bollDown
                } = bollList[bollList.length - 1]
                const {
                    upper: lastBollUp,
                    middle: lastBollMiddle,
                    lower: lastBollDown
                } = bollList[bollList.length - 2]
                return {
                    bollUp,
                    bollMiddle,
                    bollWidth: Decimal(bollUp).sub(bollMiddle).toNumber(),
                    bollDown,
                    lastBollUp,
                    lastBollMiddle,
                    lastBollDown,
                    bollList: bollList.slice(-50)
                }
            })
    }
    /**
     * 查询最近50条K线蜡烛
     * @returns K线
     */
    requestKlines () {
        const { tick, interval } = this.state
        return this.$exchange.fetchOHLCV(tick, interval)
            .then(klineList => {
                const formatedKlineList = klineList.slice(-50).map(item => ({
                    time: moment(item[0]).format(),
                    timeStamp: item[0],
                    openPrice: item[1],
                    highestPrice: item[2],
                    lowestPrice: item[3],
                    closePrice: item[4],
                    volumn: item[5],
                    middilePrice: Decimal(item[1]).add(item[4]).div(2).toNumber(),
                    color: item[1] > item[4] ? 'red': 'green'
                }))
                // formatedKlineList.forEach(item => {
                //   log(chalk[item.color](item.middilePrice))
                // })
                return formatedKlineList
            })
    }
    /**
     * 撤销所有挂单
     * @returns 撤销所有挂单
     */
    requestCancelAll () {
        LogService.clear()
        return this.$exchange.cancelAllOrders(this.state.tick)
    }
    /**
     * 设置倍率
     * @param {Number} leverage 倍率
     * @returns Promise
     */
    requestSetLeverage (leverage) {
        return this.$exchange.fapiPrivatePostLeverage({
            symbol: this.state.symbol,
            leverage
          })
    }
    /**
     * 以当前价格下单
     * @param {String} direction 方向
     * @param {Number} freeAmount 账户余额
     * @param {String} bollStatus 布林状态
     * @returns 
     */
    requestOpenOrderNow (direction, freeAmount, bollStatus, priceFactor = 10) {
        return this.requestPrice()
            .then(price => {
                const { tick, leverage, buildRatio, isMock, bollMiddle, isSetTakeProfitToBalance } = this.state
                const usdtQuantity = Decimal(leverage)
                .mul(freeAmount)
                .mul(buildRatio)
                .toNumber()

                const btcQuantity = Decimal(usdtQuantity)
                .div(price)
                .toNumber()
                let logBtcQuantity = btcQuantity
                const mulFactor = direction === OrderDirection.SELL ? 1 : -1
                const orderOpenPrice = Decimal(price).add(priceFactor * mulFactor).toNumber()

                LogService.setState(StoreName.HOLDING_ORDER_BOLL_STATUS, bollStatus)
                LogService.setState(StoreName.HOLDING_ORDER_BTC_QUANTITY, String(btcQuantity))
                LogService.setState(StoreName.OPEN_ORDER_TIMESTAMP, String(moment().valueOf()))
                LogService.setState(StoreName.HOLDING_ORDER_DIRECTION, direction)

                // 开启分仓止盈模式，则单边行情中，设1/3止盈120，1/3止盈240
                if (isSetTakeProfitToBalance) {
                    logBtcQuantity = btcQuantity * 0.33
                    const mFactor = direction === OrderDirection.BUY ? 1 : -1
                    switch (bollStatus) {
                        case BollStatus.ONESIDEUP:
                            print(`单边上涨开部分止盈单，止盈数量：1/3，止盈价格：${orderOpenPrice + 120}`, 'order')
                            this.requestEndOrder(logBtcQuantity, orderOpenPrice, orderOpenPrice + 120, OrderDirection.BUY)
                            print(`单边上涨开部分止盈单，止盈数量：1/3，止盈价格：${orderOpenPrice + 240}`, 'order')
                            this.requestEndOrder(logBtcQuantity, orderOpenPrice, orderOpenPrice + 240, OrderDirection.BUY)
                            break;
                        case BollStatus.ONESIDEDOWN:
                            print(`单边下跌开部分止盈单，止盈数量：1/3，止盈价格：${orderOpenPrice - 120}`, 'order')
                            this.requestEndOrder(logBtcQuantity, orderOpenPrice, orderOpenPrice - 120, OrderDirection.SELL)
                            print(`单边下跌开部分止盈单，止盈数量：1/3，止盈价格：${orderOpenPrice - 240}`, 'order')
                            this.requestEndOrder(logBtcQuantity, orderOpenPrice, orderOpenPrice - 240, OrderDirection.SELL)
                            break;
                        case BollStatus.NARROW:
                            print(`窄通道开止盈单，止盈位置：布林中轨，止盈价格：${bollMiddle}`, 'order')
                            this.requestEndOrder(btcQuantity, orderOpenPrice, bollMiddle, direction)
                            break;
                        case BollStatus.SHAKE:
                            print(`震荡开止盈单，止盈数量：1/2，止盈位置：布林中轨下70，止盈价格：${bollMiddle}`, 'order')
                            this.requestEndOrder(btcQuantity * 0.5, orderOpenPrice, bollMiddle + (70 * mFactor), direction)
                            print(`震荡开止盈单，止盈数量：1/2，止盈位置：布林中轨上30，止盈价格：${bollMiddle}`, 'order')
                            this.requestEndOrder(btcQuantity * 0.5, orderOpenPrice, bollMiddle + (120 * mFactor), direction)
                            break;
                        default:
                            break;
                    }
                }

                return isMock ? Promise.resolve(true) : this.$exchange.createOrder(
                    tick,
                    'LIMIT',
                    direction,
                    btcQuantity,
                    orderOpenPrice,
                    {
                        positionSide: PositionSide[direction]
                    }
                ).then(() => {
                    LogService.setState(StoreName.HOLDING_ORDER_BOLL_STATUS, bollStatus)
                    LogService.setState(StoreName.HOLDING_ORDER_BTC_QUANTITY, String(btcQuantity))
                    LogService.setState(StoreName.OPEN_ORDER_TIMESTAMP, String(moment().valueOf()))
                    LogService.setState(StoreName.HOLDING_ORDER_DIRECTION, direction)
                })
            }).catch(err => {
                console.log('开单失败', err)
                LogService.clear()
            })
    }
    
    requestBuildingOrders (direction, freeAmount, bollStatus, priceFactor = 10) {
        this.state.stopInterval()
        return this.requestPrice()
        .then(price => {
            // 打印日志
            LogService.setState(StoreName.HOLDING_ORDER_BOLL_STATUS, bollStatus)
            LogService.setState(StoreName.OPEN_ORDER_TIMESTAMP, String(moment().valueOf()))
            LogService.setState(StoreName.HOLDING_ORDER_DIRECTION, direction)

            const mulFactor = OrderDirectionFactor[direction]
            const orderOpenPrice = Decimal(price).add(priceFactor * mulFactor).toNumber()
            const buildList = MathModel.getBuildingList({
                openPrice: orderOpenPrice, 
                direction, 
                leverage: this.state.leverage,
                freeAmount,
                protectModeIndex: this.state.protectModeIndex
            })

            const buildList1 = buildList.slice(0, 16)
            const buildList2 = buildList.slice(16, 32)
            const buildList3 = buildList.slice(32)

            LogService.setJson(StoreName.BUILD_LIST, buildList)

            this.callbackForBuildOrderList(buildList1, false, () => {
                this.callbackForBuildOrderList(buildList2, true, () => {
                    this.callbackForBuildOrderList(buildList3, true, () => {
                        this.state.goInterval()
                    })
                })
            })
        })
    }
    callbackForBuildOrderList (buildList, isConect, callback = () => {}) {
        Promise.all(buildList.map(({
            direction,
            btcQuantity,
            buildPrice: orderOpenPrice
        }) => this.requestOpenOrder(direction, btcQuantity, orderOpenPrice))).then(openOrderList => {
            const sortOrderList = openOrderList.sort((a, b) => {
                if (a.info.side === OrderDirection.BUY) {
                    return a.price > b.price ? -1 : 1
                } else {
                    return a.price < b.price ? -1 : 1
                }
            })
            const _buildList = buildList.map((item, index) => {
                return {
                    ...item,
                    openInfo: {
                        ...sortOrderList[index]
                    }
                }
            })
            const cacheBuildList = LogService.getJson(StoreName.BUILD_LIST)
            LogService.setJson(StoreName.BUILD_LIST, isConect ? [...cacheBuildList, ..._buildList] : _buildList)
            callback()
        })
    }
    requestOpenOrder (direction, btcQuantity, orderOpenPrice) {
        const { tick } = this.state
        return this.$exchange.createOrder(
            tick,
            'LIMIT',
            direction,
            btcQuantity,
            orderOpenPrice,
            {
                positionSide: PositionSide[direction]
            }
        )
    }
    /**
     * 立即平仓
     * @param {Number} btcQuantity 平仓数量
     * @returns 
     */
    requestEndOrderNow (btcQuantity, stopPrice, orderType) {
        const {
            tick,
            buildItem: {
                direction
            },
            realAvgPrice: avgPrice,
            futurePrice: price
        } = this.state
        const otherSide = OrderDirection.getOtherSide(direction)
        const mulFactor = direction === OrderDirection.SELL ? -1 : 1
        const stopLossPrice = stopPrice || Decimal(price).sub(BuildConfig.STOP_GAP * mulFactor).toNumber()
        const stopLossTriggerPrice = stopPrice || Decimal(price).sub(BuildConfig.STOP_TRIGGER_GAP * mulFactor).toNumber()
        const takeProfitPrice = stopPrice || Decimal(price).add(BuildConfig.STOP_GAP * mulFactor).toNumber()
        const takeProfitTriggerPrice = stopPrice || Decimal(price).add(BuildConfig.STOP_TRIGGER_GAP * mulFactor).toNumber()
        if (
            (price > avgPrice && direction === OrderDirection.BUY)
            || (price < avgPrice && direction === OrderDirection.SELL)
            || orderType === OrderType.TAKE_PROFIT
        ) {
            // 止盈
            log('当前价格', price)
            log('止盈价格', takeProfitPrice)
            return this.$exchange.createOrder(
                tick,
                'TAKE_PROFIT_MARKET',
                otherSide,
                btcQuantity,
                takeProfitPrice,
                {
                    'positionSide': PositionSide[direction],
                    'stopPrice': takeProfitTriggerPrice,
                    // market需要此参数
                    'closePosition': true
                }
            )
        }

        if (
            (price > avgPrice && direction === OrderDirection.SELL)
            || (price < avgPrice && direction === OrderDirection.BUY)
            || orderType === OrderType.STOP
        ) {
            // 止损
            log('当前价格', price)
            log('止损价格', stopLossPrice)
            return this.$exchange.createOrder(
                tick,
                'STOP_MARKET',
                otherSide,
                btcQuantity,
                stopLossPrice,
                {
                    'positionSide': PositionSide[direction],
                    'stopPrice': stopLossTriggerPrice,
                    // market需要此参数
                    'closePosition': true
                }
                )
        }
    }
    /**
     * 限价平仓
     * @param {Number} btcQuantity 平仓数量
     * @returns 
     */
    requestEndOrder (btcQuantity, openPrice, stopPrice, direction) {
        const { tick } = this.state
        const otherSide = OrderDirection.getOtherSide(direction)
        const mulFactor = direction === OrderDirection.SELL ? -1 : 1
        const price = stopPrice
        const stopLossPrice = Decimal(price).sub(10 * mulFactor).toNumber()
        const takeProfitPrice = Decimal(price).add(10 * mulFactor).toNumber()
        if (
            (price > openPrice && direction === OrderDirection.BUY)
            || (price < openPrice && direction === OrderDirection.SELL)
        ) {
            // 止盈
            log('当前价格', price)
            log('止盈价格', takeProfitPrice)
            return this.$exchange.createOrder(
                tick,
                'TAKE_PROFIT',
                otherSide,
                btcQuantity,
                takeProfitPrice,
                {
                    'positionSide': PositionSide[direction],
                    'stopPrice': takeProfitPrice
                }
            )
        }

        if (
            (price > openPrice && direction === OrderDirection.SELL)
            || (price < openPrice && direction === OrderDirection.BUY)
        ) {
            // 止损
            log('当前价格', price)
            log('止损价格', stopLossPrice)
            return this.$exchange.createOrder(
                tick,
                'STOP',
                otherSide,
                btcQuantity,
                stopLossPrice,
                {
                    'positionSide': PositionSide[direction],
                    'stopPrice': stopLossPrice
                }
            )
        }
    }
    /**
     * 获取RSI判断
     * @returns 
     */
    requestRSI () {
        const { exchangeId, tick, interval } = this.state
        return TI
            .rsiCheck(6, 90, 10, exchangeId, tick, interval, true)
            .then(rsiCheckRes => {
                const { overBought, overSold, rsiVal } = rsiCheckRes
                return {
                    isRSIOverUp: overBought,
                    isRSIOverDown: overSold,
                    RSI: rsiVal
                }
            })
    }
    /**
     * 获取StochRSI判断
     */
    requestStochRSI () {
        const { exchangeId, tick, interval } = this.state
        return TI.stochasticrsi(
            3,
            3,
            14,
            14,
            'close',
            exchangeId,
            tick,
            interval,
            true
        ).then(stochRSIList => {
            const { stochRSI } = stochRSIList[stochRSIList.length - 1]
            
            return {
                isStochRSIOverUp: stochRSI > 98,
                isStochRSIOverDown: stochRSI < 2,
                stochRSI
            }
        })
    }
}

module.exports = new Api()